﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace MapHello.Basic
{
    static class Utility
    {
        public const int MinMapOffsetZoomLevel = 11;
        public const int MaxMapOffsetZoomLevel = 18;

        // Define other methods and classes here
        public static Task<WebResponse> ProcessHttpRequest(string url, string contentType = "", string method = "get")
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.ContentType = contentType;
            request.Method = method;

            Task<WebResponse> task = Task.Factory.FromAsync(
                request.BeginGetResponse,
                asyncResult => request.EndGetResponse(asyncResult),
                (object)null);

            return task;
        }

        public static async Task<KeyValuePair<string, byte[]>> LoadImage(string imageUrl)
        {
            var request = await Utility.ProcessHttpRequest(imageUrl);

            var responseStream = request.GetResponseStream();
            MemoryStream stream = new MemoryStream();
            await responseStream.CopyToAsync(stream);
            stream.Position = 0;
            byte[] imageBinary = stream.ToArray();



            return new KeyValuePair<string, byte[]>(request.ResponseUri.AbsoluteUri, imageBinary);
        }

        public static void OffsetMapLocation(ref double latitude, ref double longitude, double zoom)
        {
            // maps offset since 11 to 18
            int[] offsets = new int[] { 9, -2, 18, -4, 37, -8, 74, -16, 149, -33, 298, -67, 596, -135, 1193, -270 };
            if (zoom >= MinMapOffsetZoomLevel && zoom < MaxMapOffsetZoomLevel + 1)
            {
                int group = ((int)(zoom - 11)) * 2;
                int offsetX = offsets[group];
                int offsetY = offsets[group + 1];
                Debug.WriteLine("{0},{1}; Zoom:{2}; offset:{3},{4}", longitude, latitude, zoom, offsetX, offsetY);
                latitude = PixelToLatitude((LatitudeToPixel(latitude, (int)zoom) + offsetY), (int)zoom);
                longitude = PixelToLongitude((LongitudeToPixel(longitude, (int)zoom) + offsetX), (int)zoom);
                Debug.WriteLine("=> {0},{1};", longitude, latitude);
            }
        }

        private static double LongitudeToPixel(double lng, int zoom)
        {
            return (lng + 180) * (256L << zoom) / 360;
        }


        private static double PixelToLongitude(double pixelX, int zoom)
        {
            return pixelX * 360 / (256L << zoom) - 180;
        }


        private static double LatitudeToPixel(double lat, int zoom)
        {
            double siny = Math.Sin(lat * Math.PI / 180);

            double y = Math.Log((1 + siny) / (1 - siny));

            return (128 << zoom) * (1 - y / (2 * Math.PI));

        }



        private static double PixelToLatitude(double pixelY, int zoom)
        {
            double y = 2 * Math.PI * (1 - pixelY / (128 << zoom));

            double z = Math.Pow(Math.E, y);

            double siny = (z - 1) / (z + 1);

            return Math.Asin(siny) * 180 / Math.PI;
        }

    }
}
